﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Markup;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace Invini.Training
{
    public class RestrictInterfaceAction : TrainingActionBase
    {
        public PathComponentsCollection Element
        {
            get;
            set;
        }

        public RestrictInterfaceAction()
        {
            this.Element = new PathComponentsCollection();
        }
        public override void Execute()
        {
            if (this.Element == null)
            {
                this.onCompleted();
                return;
            }

            IEnumerable<object> objects = this.Element.FindElements();
            foreach (Window window in Application.Current.Windows)
                this.lockInterface(window, objects);

            this.onCompleted();
        }
        public override void Clear()
        {
            base.Clear();

            foreach (Window window in Application.Current.Windows)
                this.unlockInterface(window);
        }

        private bool lockInterface(DependencyObject parent, IEnumerable<object> activeObjects)
        {
            if (parent == null)
                return false;

            if (TrainingOptions.GetIgnore(parent))
                return true;

            List<UIElement> objectsToAlwaysBlock = new List<UIElement>();
            List<UIElement> objectsToBlock = new List<UIElement>();

            bool retValue = false;
            if (parent is Popup)
            {
                Popup popup = parent as Popup;
                if (popup.Child != null)
                {

                    if (activeObjects.Contains(popup.Child) || this.lockInterface(popup.Child, activeObjects))
                        retValue = true;
                    else
                        objectsToBlock.Add(popup.Child);
                }
            }
            else
            {
                int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < childrenCount; i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                    if (activeObjects.Contains(child))
                    {
                        retValue = true;
                        continue;
                    }

                    UIElement uiElement = child as UIElement;
                    if (uiElement == null)
                        continue;

                    if (this.lockInterface(child, activeObjects))
                        retValue = true;
                    else
                        objectsToBlock.Add(uiElement);
                }
            }

            foreach (UIElement uiElement in objectsToAlwaysBlock)
                this.lockElement(uiElement);

            if(retValue)
            {
                foreach (UIElement uiElement in objectsToBlock)
                    this.lockElement(uiElement);
            }

            return retValue;
        }

        private void unlockInterface(DependencyObject parent)
        {
            if (parent == null)
                return;

            if (parent is Popup)
            {
                Popup popup = parent as Popup;
                if (popup.Child != null)
                {
                    if (InterfaceState.GetHasState(popup.Child))
                        this.unlockElement(popup.Child);

                    this.unlockInterface(popup.Child);
                }
            }
            else
            {
                int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < childrenCount; i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                    UIElement uiElement = child as UIElement;
                    if (uiElement == null)
                        continue;

                    if (InterfaceState.GetHasState(uiElement))
                        this.unlockElement(uiElement);

                    this.unlockInterface(child);
                }
            }
        }
        
        private void lockElement(UIElement element)
        {
            InterfaceState.SetHasState(element, true);

            element.PreviewKeyDown += this.ignoreEvent;
            element.PreviewKeyUp += this.ignoreEvent;
            element.PreviewGotKeyboardFocus += this.ignoreEvent;
            element.PreviewMouseUp += this.ignoreEvent;
            element.PreviewMouseDown += this.ignoreEvent;
            element.PreviewMouseWheel += this.ignoreEvent;
            element.PreviewDragEnter += this.ignoreEvent;
            element.PreviewDragLeave += this.ignoreEvent;
            element.PreviewDragOver += this.ignoreEvent;
            element.PreviewDrop += this.ignoreEvent;
        }
        private void unlockElement(UIElement element)
        {
            InterfaceState.SetHasState(element, false);

            element.PreviewKeyDown -= this.ignoreEvent;
            element.PreviewKeyUp -= this.ignoreEvent;
            element.PreviewGotKeyboardFocus -= this.ignoreEvent;
            element.PreviewMouseUp -= this.ignoreEvent;
            element.PreviewMouseDown -= this.ignoreEvent;
            element.PreviewMouseWheel -= this.ignoreEvent;
            element.PreviewDragEnter -= this.ignoreEvent;
            element.PreviewDragLeave -= this.ignoreEvent;
            element.PreviewDragOver -= this.ignoreEvent;
            element.PreviewDrop -= this.ignoreEvent;
        }
        
        private void ignoreEvent(object sender, RoutedEventArgs e)
        {
            e.Handled = true;
        }
    }
}
